//////////////////////////////////////////////////////////////////////////////
// DCTO8DDEVICES.C   Emulation des peripheriques MO5
// Author   : Daniel Coulom - danielcoulom@gmail.com
// Web site : http://dcto8.free.fr
// Pandora port : Sylvain FABRE
//
// This file is part of DCTO8D.
//
// DCTO8D is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// DCTO8D is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with DCTO8D. If not, see <http://www.gnu.org/licenses/>.
//
//////////////////////////////////////////////////////////////////////////////

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <ctype.h>
#include "dcto8dglobal.h"

// Variable globales /////////////////////////////////////////////////////////
DIR *dmemo = NULL;  // pointeur directory pour recherche memo7
FILE *ffd = NULL;   // pointeur fichier disquette
FILE *fk7 = NULL;   // pointeur fichier k7
FILE *fprn = NULL;  // pointeur fichier imprimante
int k7bit = 0;      // puissance de 2 designant le bit k7 en cours
int k7octet;        // octet de la cassette en cours de traitement
int k7index;        // compteur du lecteur de cassette
int k7indexmax;     // compteur du lecteur de cassette en fin de bande
int k7protection;   // indicateur lecture seule pour la cassette
int fdprotection;   // indicateur lecture seule pour la disquette
char k7name[TO8D_FILENAME_SIZE]   __attribute__ ((aligned (8)));   // nom du fichier cassette
char fdname[TO8D_FILENAME_SIZE]   __attribute__ ((aligned (8)));   // nom du fichier disquette
char memoname[TO8D_FILENAME_SIZE] __attribute__ ((aligned (8)));   // nom du fichier cartouche

//6809 registers
extern char *dc6809_a, *dc6809_b;
#define CC psys->dc6809_cc
#define A *dc6809_a
#define B *dc6809_b
#define X psys->dc6809_x
#define Y psys->dc6809_y
#define S psys->dc6809_s

// Emulation imprimante //////////////////////////////////////////////////////
void Imprime()
{
 if(fprn == NULL) fprn = fopen("dcto8d-printer.txt", "ab");
 if(fprn != NULL) {fputc(B, fprn); CC &= 0xfe;};
}

// Erreur lecture/ecriture fichier qd ou fd //////////////////////////////////
void Diskerror(int n)
{
 Mputto8d(0x604e, n - 1); //erreur 53 = erreur entree/sortie
 CC |= 0x01; //indicateur d'erreur
 return;
}

// Lecture d'un secteur /////////////////////////////////////////////////////
void Readsector()
{
 char buffer[256];
 int i, j, u, p, s;
 if(ffd == NULL) {Diskerror(71); return;}
 u = Mgetto8d(0x6049) & 0xff; if(u > 03) {Diskerror(53); return;}
 p = Mgetto8d(0x604a) & 0xff; if(p != 0) {Diskerror(53); return;}
 p = Mgetto8d(0x604b) & 0xff; if(p > 79) {Diskerror(53); return;}
 s = Mgetto8d(0x604c) & 0xff; if((s == 0) || (s > 16)) {Diskerror(53); return;}
 s += 16 * p + 1280 * u;
 fseek(ffd, 0, SEEK_END);
 if((s << 8) > ftell(ffd)) {Diskerror(53); return;}
 memset( buffer, 0xE5, sizeof(buffer));
 fseek(ffd, (s - 1) << 8, SEEK_SET);
 i = ((Mgetto8d(0x604f) & 0xff) << 8) + (Mgetto8d(0x6050) & 0xff);
 if(fread(buffer, 256, 1, ffd) == 0) {Diskerror(53); return;}
 for(j = 0; j < 256; j++) Mputto8d(i++, buffer[j]);
}

// Ecriture d'un secteur /////////////////////////////////////////////////////
void Writesector()
{
 char buffer[256];
 int i, j, u, p, s;
 if(ffd == NULL) {Diskerror(71); return;}
 //erreur 72 = protection ecriture
 if(fdprotection == 1) {Diskerror(72); return;}
 u = Mgetto8d(0x6049) & 0xff; if(u > 03) {Diskerror(53); return;}
 p = Mgetto8d(0x604a) & 0xff; if(p != 0) {Diskerror(53); return;}
 p = Mgetto8d(0x604b) & 0xff; if(p > 79) {Diskerror(53); return;}
 s = Mgetto8d(0x604c) & 0xff; if((s == 0) || (s > 16)) {Diskerror(53); return;}
 s += 16 * p + 1280 * u;
 fseek(ffd, (s - 1) << 8, SEEK_SET);
 i = 256 * (Mgetto8d(0x604f) & 0xff) + (Mgetto8d(0x6050) & 0xff);
 for(j = 0; j < 256; j++) buffer[j] = Mgetto8d(i++);
 if(fwrite(buffer, 256, 1, ffd) == 0) Diskerror(53);
}

// Formatage d'un disque ////////////////////////////////////////////////////
void Formatdisk()
{
 char buffer[256];
 int i, u, fatlength;
 if(ffd == NULL) {Diskerror(71); return;}
 //erreur 72 = protection ecriture
 if(fdprotection == 1) {Diskerror(72); return;}
 u = Mgetto8d(0x6049) & 0xff; if(u > 03) return; //unite
 u = (1280 * u) << 8; //debut de l'unit� dans le fichier .fd
 fatlength = 160;     //80=160Ko, 160=320Ko
 //rem: fatlength provisoire !!!!! (tester la variable ad�quate)
 //initialisation de tout le disque avec E5
 for(i = 0; i < 256; i++) buffer[i] = 0xe5;
 fseek(ffd, u, SEEK_SET);
 for(i = 0; i < (fatlength * 8); i++)
  if(fwrite(buffer, 256, 1, ffd) == 0) {Diskerror(53); return;}
 //initialisation de la piste 20 a FF
 for(i = 0; i < 256; i++) buffer[i] = 0xff;
 fseek(ffd, u + 0x14000, SEEK_SET);
 for(i = 0; i < 16; i++)
  if(fwrite(buffer, 256, 1, ffd) == 0) {Diskerror(53); return;}
 //ecriture de la FAT
 buffer[0x00] = 0;
 buffer[0x29] = 0xfe; buffer[0x2a] = 0xfe;
 for(i = fatlength + 1; i < 256; i++) buffer[i] = 0xfe;
 fseek(ffd, u + 0x14100, SEEK_SET);
 if(fwrite(buffer, 256, 1, ffd) == 0) {Diskerror(53); return;}
}

/***************************************************/
#define SAP_HEADER_SIZE    66
#define SAP_SECT_SIZE     262
#define SAP_MAGIC_NUM    0xB3
static char *TO8PND_convert_sap_to_fg(char *inname)
{
  static unsigned char wrkbuf[SAP_SECT_SIZE+1];
  int          i, j;
  char *newname = malloc(strlen(inname) + 32);
  sprintf(newname, "%s", inname);
  char *ext = strrchr(newname, '.') ;
  ext[0] = '\0'; /* Clear the extension */
  strcat(newname, "_converted.fd"); /* Append new stuff */

  /* Perform the file conversion */
  FILE *inf  = fopen(inname, "rb+");
  FILE *outf = fopen(newname, "wb");
  fseek(inf, SAP_HEADER_SIZE, SEEK_SET); /* Skip the header */
  while (!feof( inf) )
  {
    fread(wrkbuf, 1, SAP_SECT_SIZE, inf);
    if ( wrkbuf[2] > 80 || wrkbuf[3] > 16 ) break;
    /*printf("Converting track %d sector %d\n", wrkbuf[2], wrkbuf[3]);*/
    for( i=4,j=0; i<260; i++, j++)
      wrkbuf[j] = wrkbuf[i] ^ SAP_MAGIC_NUM;
    fwrite(wrkbuf, 1, 256, outf);
  }
  fclose(inf);
  fclose(outf);

  return newname;
}

/***************************************************/
// Chargement d'un fichier fd /////////////////////////////////////////////////
void Loadfd(char *name)
{
  char filename[TEXT_MAXLENGTH];
  extern char path[][TEXT_MAXLENGTH];
  //fermeture disquette �ventuellement ouverte
  if(ffd) {fclose(ffd); ffd = NULL; fdname[0] = 0;}
  if(name[0] == 0) return;
  /* Ouverture de la nouvelle disquette */
  strcpy(fdname, name);
  strcpy(filename, path[1]);
  strcat(filename, name);
  /* Detect if SAP format and convert if needed */
  char *nametoused = filename;
  char *ext        = strrchr(filename, '.') ;
  if (ext && ((strcmp(ext, ".SAP") ==0) || (strcmp(ext, ".sap")==0) ) )
    nametoused = TO8PND_convert_sap_to_fg(filename);

  ffd = fopen(nametoused, "rb+");
  /* I we have a converted file, free the allocated memory */
  if( nametoused != filename) free(nametoused);
  /* Sanity check on file */
  if(ffd == NULL) {fdname[0] = 0; return;}
  //fdprotection = 1;
}

// Emulation cassette ////////////////////////////////////////////////////////
void Readoctetk7()
{
  int byte = 0;
  if(fk7 == NULL) { Initprog(); Erreur(11); return;}
  byte = fgetc(fk7);
  if(byte == EOF)
  {
    Initprog(); Erreur(12);
    fseek(fk7, 0, SEEK_SET); k7index = 0; Drawk7index(); return;
  }
  A = k7octet = byte; Mputto8d(0x2045, byte); k7bit = 0;
  if((ftell(fk7) & 511) == 0) {k7index = ftell(fk7) >> 9; Drawk7index();}
}

void Readbitk7()
{
   int octet = Mgetto8d(0x2045) << 1;
   if(k7bit == 0) {Readoctetk7(); k7bit = 0x80;}
   if((k7octet & k7bit)) {octet |= 0x01; A = 0xff;} else A = 0;
   Mputto8d(0x2045, octet); k7bit >>= 1;
}

void Writeoctetk7()
{
  if(fk7 == NULL) {Initprog(); Erreur(11); return;}
  if(k7protection) {Initprog(); Erreur(13); return;}
  if(fputc(A, fk7) == EOF) {Initprog(); Erreur(13); return;}
  Mputto8d(0x2045, 0);
  if((ftell(fk7) & 511) == 0) {k7index = ftell(fk7) >> 9; Drawk7index();}
}

void Loadk7(char *name)
{
  char filename[256];
  extern char path[][TEXT_MAXLENGTH];
  if(fk7) {fclose(fk7); fk7 = NULL; k7name[0] = 0;} //fermeture cassette �ventuellement ouverte
  if(name[0] == 0) return;
  strcpy(k7name, name);
  strcpy(filename, path[0]);
  strcat(filename, name);
  fk7 = fopen(filename, "rb+");
  if(fk7 == NULL) {k7name[0] = 0; return;}
  k7index = 0;
  fseek(fk7, 0, SEEK_END);
  k7indexmax = ftell(fk7) >> 9;
  fseek(fk7, 0, SEEK_SET);
  Drawk7index();
 //k7protection = 1;
}

void Rewindk7()
{
  if(fk7 == NULL) return;
  fseek(fk7, 0, SEEK_SET);
}

// Emulation cartouche memo7 /////////////////////////////////////////////////
void Loadmemo(char *name)
{
  FILE *fp = NULL;
  int i, c, carsize;
  char filename[TEXT_MAXLENGTH];
  extern char path[][TEXT_MAXLENGTH];
  strcpy(memoname, name);
  strcpy(filename, path[2]);
  strcat(filename, name);
  //ouverture du fichier memo7
  fp = fopen(filename, "rb");
  if(fp == NULL) {psys->carflags = 0; memoname[0] = 0; Hardreset(); return;}
  //chargement
  carsize = 0;
  while(((c = fgetc(fp)) != EOF) && (carsize < 0x10000)) psys->car[carsize++] = c;
  fclose(fp);
  for(i = 0; i < 0xc000; i++) psys->ram[i] = -((i & 0x80) >> 7);
  psys->cartype = 0; //cartouche <= 16 Ko
  if(carsize > 0x4000) psys->cartype = 1;   //bank switch system
  psys->carflags = 4; //cartridge enabled, write disabled, bank 0;
  Initprog();   //initialisation pour lancer la cartouche
}

// Initialisation noms de fichiers et pointeur de fonction de chargement //////
void Initfilenames(char c)
{
  extern void (*Load[]);
  k7name[0] = 0;
  fdname[0] = 0;
  memoname[0] = 0;
  Load[0] = Loadk7;
  Load[1] = Loadfd;
  Load[2] = Loadmemo;
}

/* Returns the best file loaded from K7/disc/memo */
char *TO8D_get_best_file_from_device(void)
{
  if (k7name[0] != 0 )   return k7name;
  if (fdname[0] != 0 )   return fdname;
  if (memoname[0] != 0 ) return memoname;
  return "to8system";
}

// Lecture boutons souris ////////////////////////////////////////////////////
void Readmousebutton()
{
 A = 3; if(psys->penbutton) {A = 0; CC |= 0x05;}
}

// Lecture position crayon(0) ou souris(1) ///////////////////////////////////
void Readpenxy(int device)
{
  int k;

  if((psys->xpen < 0) || (psys->xpen >= 640)) {CC |= 1; return;} //x hors limites
  if((psys->ypen < 0) || (psys->ypen >= 200)) {CC |= 1; return;} //y hors limites
  /*
  if(device == 0) //test crayon optique sur couleur tres sombre
  {
  unsigned char *p;  //pointeur dans le bitmap du point designe
  extern void *pix;  //pointeur sur le debut des pixels du bitmap
  p = pix + (y + 16) * xbitmap + (x + 16) * 2;
  if((p[0] + p[1] + p[2]) < 10) {CC |= 1; return;}
  }
  */
  k = (psys->port[0x1c] == 0x2a) ? 0 : 1; //mode 40 colonnes : x divise par 2
  if(device > 0) //souris
  {
    PUTW(0x60d8, psys->xpen >> k);
    PUTW(0x60d6, psys->ypen);
  }
  X = psys->xpen >> k;
  Y = psys->ypen;
  CC &= 0xfe;
}

